﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Drill.Configuration;
using Drill.Samples.SampleAppCommon;

namespace Drill.Samples.Sample01ConsoleApplication
{
	// -----------------------------------------------------------------------------------------
	// This is a sample app used to see how to wire up and use Drill.
	// The application itself is useless as an application. It is simply a learning tool.
	//
	// This sample application has the Drill configuration and the Unity confifuration 
	// located in the App.config file. This is one of many ways to confgiure Drill and Unity.
	//
	// The types used in this (and other) sample app are located in the SampleAppCommon project.
	//
	// Each lesson in this sample application consists of a method named LessonX where X is the 
	// sequence number of the lesson. Each lesson builds from the previous lession.
	// Each lesson method contains comments that fully explain the lesson objective and the 
	// code used in the lesson.
	//
	// Please begin by reading Lesson1 below. Then run the application after you understand the 
	// code in the lesson. Once you understand the lesson, please move on to the next lesson.
	// -----------------------------------------------------------------------------------------

	class Program
	{
		static void Main(string[] args)
		{
			try
			{
				new Program().Run();
			}
			catch(Exception ex)
			{
				Console.WriteLine(ex.ToString());
			}
		}

		private void Run()
		{
			Lesson1();
			Lesson2();
			Lesson3();
			Lesson4();
			Lesson5();
			Lesson6();
			Lesson7();
			Lesson8();
			Lesson9();
			Lesson10();
		}

		private void Lesson1()
		{
			WriteLessonHeader(1);

			// Lesson 1.
			// Load Drill configuration from the App.config file and create an instance 
			// of IDependencyResolver and use it to resolve objects.
			//
			// This is the easiest way to create a new instance of a dependency resolver.
			// Load the configuration of the named dependency resolver, "MyResolver", 
			// from the default configuration file (App.config).
			// Call the Create method of the DependencyResolverManager passing in the config.
			// 
			IDependencyResolverConfiguration config = ConfigurationLoader.LoadDependencyResolverConfig("MyResolver");
			IDependencyResolver dependencyResolver = DependencyResolverManager.Create(config);

			// Once we have an IDependencyResolver instance, we can use it to retrieve 
			// instances of objects from the underlying DrillBit component, which in this 
			// case is Drill.DrillBits.Unity, adapting Microsoft Unity to Drill.
			// Since we are using Unity, the types are resolved by the Unity container.
			// In this sample app, Unity is configured in the App.config. The DrillBit 
			// component is named "MyUnityContainer" and the component will create a Unity 
			// container named "MyUnityContainer" which is configured by the config source 
			// method, ConfigFile, which means the default config file (e.g. App.config, Web.config).
			// Because Unity is being configured by the config file, there must be a container 
			// element with the name "MyUnityContainer".
			//
			// Why do we have to load the configuration first before calling DependencyResolverManager.Create?
			// There are multiple ways to create a Drill configuration object graph.
			// The ConfigurationLoader class is useful when loading Drill configuration from a .NET 
			// config file such as App.config or Web.config or from an external .NET config file.
			// The DependencyResolverManager does not make assumptions on how the configuration should 
			// be created because that is not it's responsibility. In fact, the DependencyResolverManager
			// class is a convenience and is not required to be used at all (although it is recommended 
			// because it will save you time and save you the trouble of learning the Drill internals).
			//
			// At this time, please take a look at the App.config file. It contains two config sections that 
			// are of interest. The first config section is <drill>. This is the drill config that is being 
			// used for this lesson. This is the minimal amount of Drill configuration that is required.
			// The Drill configuration can become much more complicated that what is used for this sample app 
			// depending on what you need to do with Drill. For now, we are keeping things simple and using the 
			// default settings for most Drill features.
			// The second config section of interest is <unity>. This is the configuration of Unity including 
			// its container. The objects configured in the Unity container come from the SampleAppCommon project.
			//
			// All of the lessons will use the ResolveObjectsAndWriteResults to resolve objects.
			// Please take some time to look at this method an see how you use a dependency resolver 
			// instance to get the instances of the object your application requires.
			// It is very straight froward and is very similar to many other dependency injection containers 
			// (such as Mirosoft Unity) and the Microsoft's Common Service Locator.
			// Although the lessons in this application use Unity as the underlying DI container, Drill 
			// is very extensible and can work with any underlying container or service locator. More on this later.
			//
			// Have a look at the ResolveObjectsAndWriteResults method now.
			ResolveObjectsAndWriteResults(dependencyResolver);
		}

		private void Lesson2()
		{
			WriteLessonHeader(2);

			// Lesson 2.
			// Load Drill configuration from an external .NET config file and create 
			// an instance of IDependencyResolver and use it to resolve objects.
			//
			// Load the configuration of the named dependency resolver, "MyResolver", 
			// explicitly from a .NET configuration file by specifying the full path.
			// This is useful when loading config from an external or non-default .NET config 
			// file, but in this lesson, we will just specify the path to the App.config
			// file to simulate using an external file.
			// 
			// Get the full path to the App.config to simulate an external .NET config file.
			string configFilename = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

			// Load the external config file using the ConfigurationLoader helper class.
			IDependencyResolverConfiguration config = 
				ConfigurationLoader.LoadDependencyResolverConfig("MyResolver", configFilename);

			// Use the DependencyResolverManager to create a depedency resolver from the config.
			IDependencyResolver dependencyResolver = DependencyResolverManager.Create(config);

			ResolveObjectsAndWriteResults(dependencyResolver);
		}

		private void Lesson3()
		{
			WriteLessonHeader(3);

			// Lesson 3.
			// Load Drill configuration from an XML file and create 
			// an instance of IDependencyResolver and use it to resolve objects.
			//
			// Load the configuration of the named dependency resolver, "MyResolver", 
			// explicitly from an XML file by specifying the full path.
			// In this lesson, we will just specify the path to the App.config 
			// file to simulate using an XML file.
			//
			// The configuration schema is basically the same as what is used for 
			// .NET config files except that all of the elements hierarchically above 
			// the dependencyResolver element are not necessarily required. 
			// The sectionGroup and section elements are not required at all when loading 
			// Drill configuration from an XML file.
			// 
			// Get the full path to the App.config to simulate an XML file containing Drill configuration.
			string configFilename = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

			// Create an XmlConfigurationReader instance that will read Drill configuration from 
			// the specified XML file.
			XmlConfigurationReader reader = XmlConfigurationReader.FromFile(configFilename);

			// Load the dependency resolver configuration from the XML file using the 
			// XmlConfigurationReader instance created above.
			IDependencyResolverConfiguration config = reader.ReadDependencyResolverConfiguration("MyResolver");

			// Use the DependencyResolverManager to create a depedency resolver from the config.
			IDependencyResolver dependencyResolver = DependencyResolverManager.Create(config);

			ResolveObjectsAndWriteResults(dependencyResolver);
		}

		private void Lesson4()
		{
			WriteLessonHeader(4);

			// Lesson 4.
			// Load Drill configuration from a stream containing Drill XML configuration  
			// and create an instance of IDependencyResolver and use it to resolve objects.
			//
			// Load the configuration of the named dependency resolver, "MyResolver", 
			// explicitly from a stream containing Drill XML configuration.
			// In this lesson, we will create a FileStream from the path to the App.config 
			// file to use as the stream.
			// 
			// Get the full path to the App.config to simulate an XML file containing Drill configuration.
			string configFilename = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

			// Declare the config variable here since we will set in from within the usng block below.
			IDependencyResolverConfiguration config;

			// Create a new FileStream from the specified XML file.
			using (FileStream stream = new FileStream(configFilename, FileMode.Open, FileAccess.Read))
			{
				// Create an XmlConfigurationReader instance that will read Drill configuration from 
				// the specified XML file.
				XmlConfigurationReader reader = XmlConfigurationReader.FromStream(stream);

				// Load the dependency resolver configuration from the XML file using the 
				// XmlConfigurationReader instance created above.
				config = reader.ReadDependencyResolverConfiguration("MyResolver");
			}

			// Use the DependencyResolverManager to create a depedency resolver from the config.
			IDependencyResolver dependencyResolver = DependencyResolverManager.Create(config);

			ResolveObjectsAndWriteResults(dependencyResolver);
		}

		private void Lesson5()
		{
			WriteLessonHeader(5);

			// Lesson 5.
			// Load Drill configuration from an XDocument instance containing Drill XML configuration  
			// and create an instance of IDependencyResolver and use it to resolve objects.
			//
			// Load the configuration of the named dependency resolver, "MyResolver", 
			// explicitly from an XDocument containing Drill XML configuration.
			// In this lesson, we will create an XDocument from the path to the App.config file.
			// 
			// Get the full path to the App.config to simulate an XML file containing Drill configuration.
			string configFilename = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

			// Create an XDocument instance loaded from the config file.
			XDocument xdoc = XDocument.Load(configFilename);

			// Create an XmlConfigurationReader instance that will read Drill configuration from 
			// the specified XDocument instance.
			XmlConfigurationReader reader = XmlConfigurationReader.FromXDocument(xdoc);

			// Load the dependency resolver configuration from the XML file using the 
			// XmlConfigurationReader instance created above.
			IDependencyResolverConfiguration config = reader.ReadDependencyResolverConfiguration("MyResolver");

			// Use the DependencyResolverManager to create a depedency resolver from the config.
			IDependencyResolver dependencyResolver = DependencyResolverManager.Create(config);

			ResolveObjectsAndWriteResults(dependencyResolver);
		}

		private void Lesson6()
		{
			WriteLessonHeader(6);

			// Lesson 6.
			// Load Drill configuration from an embedded manifest resource containing Drill XML configuration  
			// and create an instance of IDependencyResolver and use it to resolve objects.
			//
			// Load the configuration of the named dependency resolver, "MyResolver", 
			// explicitly from an embedded manifest resource containing Drill XML configuration.
			// 
			// Get the name of the embedded manifest resource containing Drill configuration.
			const string resourceName = "Drill.Samples.Sample01ConsoleApplication.EmbeddedDrillConfig.xml";

			// Create an XmlConfigurationReader instance that will read Drill configuration from 
			// the specified manifest resource.
			XmlConfigurationReader reader = XmlConfigurationReader.FromManifestResource(resourceName);

			// Load the dependency resolver configuration from the XML file using the 
			// XmlConfigurationReader instance created above.
			IDependencyResolverConfiguration config = reader.ReadDependencyResolverConfiguration("MyResolver");

			// Use the DependencyResolverManager to create a depedency resolver from the config.
			IDependencyResolver dependencyResolver = DependencyResolverManager.Create(config);

			ResolveObjectsAndWriteResults(dependencyResolver);
		}

		private void Lesson7()
		{
			WriteLessonHeader(7);

			// Lesson 7.
			// Load Drill configuration from a literal string containing Drill XML configuration  
			// and create an instance of IDependencyResolver and use it to resolve objects.
			//
			// Load the configuration of the named dependency resolver, "MyResolver", 
			// explicitly from a literal string containing Drill XML configuration.
			// We will create the lietral string from App.config file. The XML configuration 
			// contained in a literal string can come from any source.
			// 
			// Get the full path to the App.config to simulate an XML file containing Drill configuration.
			string configFilename = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

			string xmlConfig = File.ReadAllText(configFilename);

			// Create an XmlConfigurationReader instance that will read Drill configuration from 
			// the literal string.
			XmlConfigurationReader reader = XmlConfigurationReader.FromXmlString(xmlConfig);

			// Load the dependency resolver configuration from the XML file using the 
			// XmlConfigurationReader instance created above.
			IDependencyResolverConfiguration config = reader.ReadDependencyResolverConfiguration("MyResolver");

			// Use the DependencyResolverManager to create a depedency resolver from the config.
			IDependencyResolver dependencyResolver = DependencyResolverManager.Create(config);

			ResolveObjectsAndWriteResults(dependencyResolver);
		}

		private void Lesson8()
		{
			WriteLessonHeader(8);

			// Lesson 8.
			// Learn more about the DependencyResolverManager class (IsRegistered, 
			// Register and Reset methods, the Resolvers property).
			//
			// The DependencyResolverManager class is a static class that provides 
			// conveniece when creating dependency resolver instances. The class 
			// also provides access to a centralized dictionary of registered 
			// dependency resolver instances. You do not have to use registered 
			// dependency resolvers. It's optional. You also do not want to create 
			// a new dependency resolver instance every time you need to use it. 
			// Ideally, you should create a depedency resolver once and reuse it.
			// You have two options: you can manage the lifetime of the dependency 
			// resolver instance your self or your can register your dependency resolver 
			// instance(s) with the DependencyResolverManager and then look it up when 
			// you need to use it. The DependencyResolverManager class is a static 
			// singleton which means there is one unique instance per AppDomain.
			// 
			// There are two ways to register a dependency resolver instance with the 
			// DependencyResolverManager: the CreateAndRegister method or the Register method.
			//
			// Let's experiement with registered dependency resolvers.
			//

			// Create a new dependency resolver instance from the Drill configuration in the App.config file.
			IDependencyResolverConfiguration config = ConfigurationLoader.LoadDependencyResolverConfig("MyResolver");
			IDependencyResolver myResolver = DependencyResolverManager.Create(config);
			ResolveObjectsAndWriteResults(myResolver);

			// Check to see if it is registered with the DependencyResolverManager.
			bool isRegistered = DependencyResolverManager.IsRegistered("MyResolver");
			Console.WriteLine("  Check 1: DependencyResolverManager.IsRegistered(\"MyResolver\") == {0}", isRegistered);
			
			// At this point, isRegistered should be false.
			// Let's register myResolver with the DependencyResolverManager.
			DependencyResolverManager.Register("MyResolver", myResolver);

			// Check to see if it is registered with the DependencyResolverManager.
			isRegistered = DependencyResolverManager.IsRegistered("MyResolver");
			Console.WriteLine("  Check 2: DependencyResolverManager.IsRegistered(\"MyResolver\") == {0}", isRegistered);

			// At this point, isRegistered should be true.
			// Let's prove it by getting a reference to the registered instance.
			IDependencyResolver lookedUpResolver = DependencyResolverManager.Resolvers["MyResolver"];
			bool areSame = Equals(myResolver, lookedUpResolver);
			Console.WriteLine("  Check 3: Object.Equals(myResolver, lookedUpResolver) == {0}", areSame);

			// At this point, areSame should be true because the lookedUpResolver is a 
			// reference to the exact same object instance as myResolver.
			//
			// Notice that to get a reference to a registered dependency resolver from the 
			// DependencyResolverManager we access the Resolvers property which returns a 
			// read-only dictionary from which we call the indexer passing the in the registered 
			// name. We could also access other non-modifying members of the Resolvers dictionary 
			// such as the Keys property, TryGetValue method or the Values property.
			// The dictionary is read only and if members that would modify the dictionary are 
			// called, a NotSupportedException will be thrown. In a future version, the dictionary 
			// will likely be derived from IReadOnlyDictionary which is only supported in .NET 4.5.
			//
			// Because the following lessons will use DependencyResolverManager with 
			// similar configuration, objects and names, we need to reset the state of 
			// DependencyResolverManager so that we do not try to register duplicate names.
			// The DependencyResolverManager class provides the Reset method that accomplishes 
			// this requirement.
			DependencyResolverManager.Reset();

			Console.WriteLine();

			//
			// This concludes Lesson 8.
		}

		private void Lesson9()
		{
			WriteLessonHeader(9);

			// Lesson 9.
			// Learn even more about the DependencyResolverManager class (CreateAndRegister method).
			//
			// Lesson 1 showed you how to create a new dependency resolver instance 
			// using the Create method of the DependencyResolverManager class.
			//
			// Lesson 8 showed you how to check for a registered dependency resolver, 
			// how to register a dependency resolver instance and how to look up a 
			// registered dependency resolver instance.
			// 
			// Lesson 9 will show you how to use the CreateAndRegister method.
			//

			// Load the dependency resolver configuration from the Drill configuration in the App.config file.
			IDependencyResolverConfiguration config = ConfigurationLoader.LoadDependencyResolverConfig("MyResolver");

			// Use the DependencyResolverManager's CreateAndRegister method to create a new 
			// dependency resolver instance and register it with the DependencyResolverManager 
			// all in one step.
			IDependencyResolver myResolver = DependencyResolverManager.CreateAndRegister(config);

			// Resolve some object to prove that the dependency resolver instance works properly.
			ResolveObjectsAndWriteResults(myResolver);

			// Check to see if it is registered with the DependencyResolverManager.
			bool isRegistered = DependencyResolverManager.IsRegistered("MyResolver");
			Console.WriteLine("  Check 1: DependencyResolverManager.IsRegistered(\"MyResolver\") == {0}", isRegistered);

			// At this point, isRegistered should be true.
			// But how did we know what name to use when calling the IsRegistered method?
			// We did not specify a name when we called CreateAndRegister.
			// When CreateAndRegister is called and the name argument is not specified or is null, 
			// empty or whitespace, the configured name of the dependency resolver is used as the 
			// registration key with the DependencyResolverManager. If you want to use a different 
			// name, simply specify the name argument when callng CreateAndRegister.
			// Let's try it now.

			// Check to see if it is registered with the DependencyResolverManager.
			isRegistered = DependencyResolverManager.IsRegistered("AnotherResolver");
			Console.WriteLine("  Check 2: DependencyResolverManager.IsRegistered(\"AnotherResolver\") == {0}", isRegistered);

			// At this point, isRegistered should be false.
			// Now let's call CreateAndRegister and specify "AnotherResolver" as the name 
			// but we will use the same configuration as "MyResolver".
			IDependencyResolver anotherResolver = DependencyResolverManager.CreateAndRegister(config, "AnotherResolver");

			// Check to see if it is registered with the DependencyResolverManager.
			isRegistered = DependencyResolverManager.IsRegistered("AnotherResolver");
			Console.WriteLine("  Check 3: DependencyResolverManager.IsRegistered(\"AnotherResolver\") == {0}", isRegistered);

			// At this point, isRegistered should be true.
			// Let's prove it by getting a reference to the registered instance.
			IDependencyResolver lookedUpResolver = DependencyResolverManager.Resolvers["AnotherResolver"];
			bool areSame = Equals(anotherResolver, lookedUpResolver);
			Console.WriteLine("  Check 4: Object.Equals(anotherResolver, lookedUpResolver) == {0}", areSame);

			// At this point, areSame should be true because the lookedUpResolver is a 
			// reference to the exact same object instance as anotherResolver.
			// But even though anotherResolver was created using the same configuration 
			// as myResolver, they are not the same instance. Let's prove it.
			areSame = Equals(myResolver, anotherResolver);
			Console.WriteLine("  Check 5: Object.Equals(myResolver, anotherResolver) == {0}", areSame);

			// At this point, isRegistered should be false.
			// myResolver and anotherResolver are different instances, but because they were created 
			// from the same configuration, they function exactly the same.

			Console.WriteLine();

			// Resolve some object to prove that the dependency resolver instance works properly.
			ResolveObjectsAndWriteResults(anotherResolver);

			// Reset the DependencyResolverManager.
			DependencyResolverManager.Reset();

			Console.WriteLine();

			//
			// This concludes Lesson 9.
		}

		private void Lesson10()
		{
			WriteLessonHeader(10);

			// Lesson 10.
			// Wrapping up the DependencyResolverManager class (the Unregister method).
			//
			// In the previous lessons you learned that you can use the DependencyResolverManager 
			// to create new instances of dependency resolvers, how to check to see if they are 
			// registered with the DependencyResolverManager, how to register them and how to 
			// get a reference to a registered instance. You also learned, out of necessity, 
			// that you can reset the state of the DependencyResolverManager which releases all 
			// registered instances.
			//
			// In this lesson, you will learn that you can also unregister a registered dependency 
			// resolver instance, if necessary.
			//

			// Load the dependency resolver configuration from the Drill configuration in the App.config file.
			IDependencyResolverConfiguration config = ConfigurationLoader.LoadDependencyResolverConfig("MyResolver");

			// Use the DependencyResolverManager's CreateAndRegister method to create a new 
			// dependency resolver instance and register it with the DependencyResolverManager 
			// all in one step.
			IDependencyResolver myResolver = DependencyResolverManager.CreateAndRegister(config);

			Console.WriteLine("  Created and registered \"MyResolver\"");

			// Check to see if it is registered with the DependencyResolverManager.
			bool isRegistered = DependencyResolverManager.IsRegistered("MyResolver");
			Console.WriteLine("  Check 1: DependencyResolverManager.IsRegistered(\"MyResolver\") == {0}", isRegistered);

			// At this point, isRegistered should be true.
			// Resolve some object to prove that the dependency resolver instance works properly.
			ResolveObjectsAndWriteResults(DependencyResolverManager.Resolvers["MyResolver"]);

			// Now we will add another dependency resolver instance.
			IDependencyResolver anotherResolver = DependencyResolverManager.CreateAndRegister(config, "AnotherResolver");

			Console.WriteLine("  Created and registered \"AnotherResolver\"");

			// Check to see if it is registered with the DependencyResolverManager.
			isRegistered = DependencyResolverManager.IsRegistered("AnotherResolver");
			Console.WriteLine("  Check 2: DependencyResolverManager.IsRegistered(\"AnotherResolver\") == {0}", isRegistered);

			// At this point, isRegistered should be true.
			// Resolve some object to prove that the dependency resolver instance works properly.
			ResolveObjectsAndWriteResults(DependencyResolverManager.Resolvers["AnotherResolver"]);

			// Now we should have two registered dependency resolver instances.
			// Let's prove it.
			int count = DependencyResolverManager.Resolvers.Count;
			Console.WriteLine("  Check 3: DependencyResolverManager.Resolvers.Count == {0}", count);

			Console.WriteLine("  Check 4: DependencyResolverManager.Keys.ToList().ForEach:");
			DependencyResolverManager.Resolvers.Keys.ToList().ForEach(key =>
				{
					Console.WriteLine("    \"{0}\"", key);
				});

			// Now we will unregister myResolver.
			DependencyResolverManager.Unregister("MyResolver");

			Console.WriteLine("  Unregistered \"MyResolver\"");

			// Now we should have one registered dependency resolver instance, "AnotherResolver".
			// Let's prove it.
			count = DependencyResolverManager.Resolvers.Count;
			Console.WriteLine("  Check 5: DependencyResolverManager.Resolvers.Count == {0}", count);

			Console.WriteLine("  Check 6: DependencyResolverManager.Keys.ToList().ForEach:");
			DependencyResolverManager.Resolvers.Keys.ToList().ForEach(key =>
			{
				Console.WriteLine("    \"{0}\"", key);
			});

			isRegistered = DependencyResolverManager.IsRegistered("MyResolver");
			Console.WriteLine("  Check 7: DependencyResolverManager.IsRegistered(\"MyResolver\") == {0}", isRegistered);

			isRegistered = DependencyResolverManager.IsRegistered("AnotherResolver");
			Console.WriteLine("  Check 8: DependencyResolverManager.IsRegistered(\"AnotherResolver\") == {0}", isRegistered);

			// At this point, isRegistered for "MyResolver" should be false and 
			// isRegistered for "AnotherResolver" should be true.

			Console.WriteLine();

			// Resolve some object to prove that the dependency resolver instance works properly.
			ResolveObjectsAndWriteResults(anotherResolver);

			// Reset the DependencyResolverManager.
			DependencyResolverManager.Reset();

			Console.WriteLine();

			//
			// Of course, you do not have to use registered dependency resolvers with 
			// the DependencyResolverManager. You can use your own methods of storing 
			// a reference that works for you and your application.
			// You do not have to use DependencyResolverManager to create dependency resolver 
			// instances. You can simply use the appropriate IDependnecyResolverFactory to 
			// create the instance of the required dependency resolver instance. More on this later.
			//
			// This concludes Lesson 10.
		}

		private void ResolveObjectsAndWriteResults(IDependencyResolver dependencyResolver)
		{
			// Side Lesson: Resolving objects using a dependency resolver; The IDependencyResolver interface.
			//
			// A depednency resolver is an object that implements the IDependencyResolver interface.
			// This interface is used to resolve object instances.
			// It is similar to other dependency injection frameworks and the Common Service Locator.
			// This is intentional. The interface supports resolving types using generic and non-generic 
			// method variants. 
			//
			// The IDependencyResolver generic methods are:
			//    TTargetType GetInstance<TTargetType>();
			//    TTargetType GetInstance<TTargetType>(string name);
			//    IEnumerable<TTargetType> GetAllInstances<TTargetType>();
			//
			// The IDependencyResolver non-generic methods are:
			//    object GetInstance(Type targetType);
			//    object GetInstance(Type targetType, string name);
			//    IEnumerable GetAllInstances(Type targetType);
			//
			// Please do read the IDependencyResolver interface source code as each method 
			// is documented there. If you have worked with other DI frameworks or the 
			// Common Service Locator, the IDependencyResolver interface should be very famailliar.
			//
			// ------------------------------------------------------------------------------------------------
			// Get instances of IColor, a very simple object with no dependencies.
			//
			// Get the default instance of IColor by calling the generic GetInstance<T>() method.
			IColor defaultColor = dependencyResolver.GetInstance<IColor>();
			Console.WriteLine("  Retrieved default IColor instance using generic method, \n    GetInstance<IColor>()");
			Console.WriteLine("      defaultColor.Name = {0}", defaultColor.Name);
			Console.WriteLine();

			// Get a named instance of IColor by calling the generic GetInstance<T>(string name) method.
			IColor orangeColor = dependencyResolver.GetInstance<IColor>("Orange");
			Console.WriteLine("  Retrieved \"Orange\" IColor instance using generic method with name, \n    GetInstance<IColor>(\"Orange\")");
			Console.WriteLine("      orangeColor.Name = {0}", orangeColor.Name);
			Console.WriteLine();

			// Get all instances of IColor by calling the generic GetAllInstances<T>() method.
			IEnumerable<IColor> allColors = dependencyResolver.GetAllInstances<IColor>();
			Console.WriteLine("  Retrieved all IColor instances using generic method, \n    GetAllInstances<IColor>()");
			allColors.ToList().ForEach(color => {Console.WriteLine("      color.Name = {0}", color.Name);});
			Console.WriteLine();

			// Get the default instance of IColor by calling the non-generic GetInstance(Type type) method.
			IColor defaultColor2 = (IColor)dependencyResolver.GetInstance(typeof(IColor));
			Console.WriteLine("  Retrieved default IColor instance using non-generic method, \n    GetInstance(typeof(IColor))");
			Console.WriteLine("      defaultColor2.Name = {0}", defaultColor2.Name);
			Console.WriteLine();

			// Get a named instance of IColor by calling the non-generic GetInstance(Type type, string name) method.
			IColor whiteColor = (IColor)dependencyResolver.GetInstance(typeof(IColor), "White");
			Console.WriteLine("  Retrieved \"White\" IColor named instance using non-generic method, \n    GetInstance(typeof(IColor), \"White\")");
			Console.WriteLine("      defaultColor2.Name = {0}", whiteColor.Name);
			Console.WriteLine();

			// Get all instances of IColor by calling the non-generic GetAllInstances(Type type) method.
			allColors = dependencyResolver.GetAllInstances(typeof(IColor)).Cast<IColor>();
			Console.WriteLine("  Retrieved all IColor instances using non-generic method, \n    GetAllInstances(typeof(IColor))");
			allColors.ToList().ForEach(color => { Console.WriteLine("      color.Name = {0}", color.Name); });
			Console.WriteLine();

			//
			// ------------------------------------------------------------------------------------------------
			// Get instances of IColorScheme, a more cmoplex object with dependencies.
			//
			// Get the default instance of IColorScheme by calling the generic GetInstance<T>() method.
			IColorScheme colorScheme = dependencyResolver.GetInstance<IColorScheme>();
			Console.WriteLine("  Retrieved default IColorScheme instance using generic method, \n    GetInstance<IColorScheme>()");
			WriteColorScheme(colorScheme);

			// Get a named instance of IColorScheme by calling the generic GetInstance<T>(string name) method.
			colorScheme = dependencyResolver.GetInstance<IColorScheme>("Halloween");
			Console.WriteLine("  Retrieved \"Halloween\" IColorScheme named instance using generic method w/ name, \n    GetInstance<IColorScheme>(\"Halloween\")");
			WriteColorScheme(colorScheme);

			// Get all instances of IColorScheme by calling the generic GetAllInstances<T>() method.
			IEnumerable<IColorScheme> allColorSchemes = dependencyResolver.GetAllInstances<IColorScheme>();
			Console.WriteLine("  Retrieved all IColorScheme instances using generic method, \n    GetAllInstances<IColorScheme>()");
			WriteAllColorSchemes(allColorSchemes);

			// Get the default instance of IColorScheme by calling the non-generic GetInstance(Type type) method.
			colorScheme = (IColorScheme)dependencyResolver.GetInstance(typeof(IColorScheme));
			Console.WriteLine("  Retrieved default IColorScheme instance using non-generic method, \n    GetInstance(typeof(IColorScheme))");
			WriteColorScheme(colorScheme);

			// Get a named instance of IColorScheme by calling the non-generic GetInstance(Type type, string name) method.
			colorScheme = (IColorScheme)dependencyResolver.GetInstance(typeof(IColorScheme), "Holiday");
			Console.WriteLine("  Retrieved \"Holiday\" IColorScheme instance using non-generic method w/ name, \n    GetInstance(typeof(IColorScheme), \"Holiday\")");
			WriteColorScheme(colorScheme);

			// Get all instances of IColorScheme by calling the non-generic GetAllInstances(Type type) method.
			allColorSchemes = dependencyResolver.GetAllInstances(typeof(IColorScheme)).Cast<IColorScheme>();
			Console.WriteLine("  Retrieved all IColorScheme instances using non-generic method, \n    GetAllInstances(typeof(IColorScheme))");
			WriteAllColorSchemes(allColorSchemes);
		}

		#region " Utility Methods - Nothing Important Here Concerning the Lessons "

		private void WriteLessonHeader(int lessonNumber)
		{
			Console.WriteLine(new string('-', 80));
			Console.WriteLine("Executing Lesson {0}", lessonNumber);
			Console.WriteLine();
		}

		private void WriteAllColorSchemes(IEnumerable<IColorScheme> colorSchemes)
		{
			colorSchemes.ToList().ForEach(WriteColorScheme);
		}

		private void WriteColorScheme(IColorScheme colorScheme)
		{
			Console.WriteLine("      colorScheme.Name                = {0}", colorScheme.Name);
			Console.WriteLine("      colorScheme.PrimaryColor.Name   = {0}", colorScheme.PrimaryColor.Name);
			Console.WriteLine("      colorScheme.SecondaryColor.Name = {0}", colorScheme.SecondaryColor.Name);
			Console.WriteLine();
		}

		#endregion " Utility Methods - Nothing Important Here Concerning the Lessons "
	}
}
